package com.example.smallandroidcodetester;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;

import android.app.Activity;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.BitmapFactory.Options;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PointF;
import android.media.FaceDetector;
import android.media.FaceDetector.Face;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;

public class FaceDetection2 extends Activity {

	private static final int TAKE_PICTURE_CODE = 100;
	private static final int MAX_FACES = 5;
	private Bitmap originalBitmap = null;
	private ImageView imageView;
	private PointF midPoint = new PointF();
	private float eyeDistance = 0.0f;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.face_detection_main_2);
		imageView = (ImageView) findViewById(R.id.FamilyImage);
		((Button) findViewById(R.id.take_picture)).setOnClickListener(btnClick);
		((Button) findViewById(R.id.detect_face)).setOnClickListener(btnClick);
	}

	private View.OnClickListener btnClick = new View.OnClickListener() {
		@Override
		public void onClick(View v) {
			switch (v.getId()) {
			case R.id.take_picture:
				loadPictureFromAssets();
				break;
			case R.id.detect_face:
				detectFaces();
				break;
			}
		}
	};

	private void loadPictureFromAssets() {
		AssetManager am = getAssets();
		InputStream is = null;
		try {
			is = am.open("jack.jpg");
			BitmapFactory.Options options = new Options();
			options.inPreferredConfig = Config.RGB_565;
			originalBitmap = BitmapFactory.decodeStream(is, null, options);
			((ImageView) findViewById(R.id.FamilyImage)).setImageBitmap(originalBitmap);
		} catch (IOException e) {
			Log.e("ERROR", "ERROR : " + e + " = " + e.getMessage());
		}
	}

	private void detectFaces() {
		if (null != originalBitmap) {
			Bitmap mutableBitmap = null;
			Face[] faces = new Face[2];
			FaceDetector faceDetector = new FaceDetector(590, 725, 2);
			int faceCount = -1;
			float confidence = 0.0f;

			Paint ditherPaint = new Paint();
			Paint drawPaint = new Paint();
			ditherPaint.setDither(true);
			drawPaint.setColor(Color.RED);
			drawPaint.setStyle(Paint.Style.STROKE);
			drawPaint.setStrokeWidth(2);
			Canvas canvas = new Canvas();
			try {
				mutableBitmap = convertToMutable(originalBitmap);
				canvas.setBitmap(mutableBitmap);
				canvas.drawBitmap(mutableBitmap, 0, 0, ditherPaint);
				faceCount = faceDetector.findFaces(mutableBitmap, faces);
				System.out.println("faceCount : " + faceCount);
				System.out.println("facesLength : " + faces.length);
			} catch (Exception e) {
				Log.e("---", "ERROR : " + e + " = " + e.getMessage());
			}

			String filepath = Environment.getExternalStorageDirectory() + "/facedetect" + System.currentTimeMillis() + ".jpg";

			try {
				if (faceCount > 0) {
					for (int index = 0; index < faceCount; ++index) {
						// Get color of both of the eyes index0 = left eye / index1 = right eye
						int[] eyeColors = detectEyesColor(mutableBitmap, faces[index]);
						Paint leftEyeDrawPaint = new Paint();
						Paint rightEyeDrawPaint = new Paint();

						leftEyeDrawPaint.setColor(eyeColors[0]);
						leftEyeDrawPaint.setStyle(Paint.Style.STROKE);
						leftEyeDrawPaint.setStrokeWidth(2);
						rightEyeDrawPaint.setColor(eyeColors[1]);
						rightEyeDrawPaint.setStyle(Paint.Style.STROKE);
						rightEyeDrawPaint.setStrokeWidth(2);

						canvas.drawCircle(Math.round(midPoint.x - eyeDistance / 2), Math.round(midPoint.y) + 15, 1, drawPaint);

						// draw a circle with the left eye color
						canvas.drawCircle(midPoint.x - eyeDistance / 2, midPoint.y + 30, 10, leftEyeDrawPaint);
						// draw a circle with the right eye color
						canvas.drawCircle(midPoint.x + eyeDistance / 2, midPoint.y + 30, 10, rightEyeDrawPaint);

					}
				}
			} catch (Exception e) {

			}

			try {
				FileOutputStream fos = new FileOutputStream(filepath);

				mutableBitmap.compress(CompressFormat.JPEG, 90, fos);

				fos.flush();
				fos.close();
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}

			imageView.setImageBitmap(mutableBitmap);
		}
	}

	/**
	 * Converts a immutable bitmap to a mutable bitmap. This operation doesn't allocates more memory that there is already allocated.
	 * 
	 * @param imgIn
	 *            - Source image. It will be released, and should not be used more
	 * @return a copy of imgIn, but muttable.
	 */
	public static Bitmap convertToMutable(Bitmap imgIn) {
		try {
			// this is the file going to use temporally to save the bytes.
			// This file will not be a image, it will store the raw image data.
			File file = new File(Environment.getExternalStorageDirectory() + File.separator + "temp.tmp");

			// Open an RandomAccessFile
			// Make sure you have added uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
			// into AndroidManifest.xml file
			RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");

			// get the width and height of the source bitmap.
			int width = imgIn.getWidth();
			int height = imgIn.getHeight();
			Config type = imgIn.getConfig();

			// Copy the byte to the file
			// Assume source bitmap loaded using options.inPreferredConfig = Config.ARGB_8888;
			FileChannel channel = randomAccessFile.getChannel();
			MappedByteBuffer map = channel.map(MapMode.READ_WRITE, 0, imgIn.getRowBytes() * height);
			imgIn.copyPixelsToBuffer(map);
			// recycle the source bitmap, this will be no longer used.
			imgIn.recycle();
			System.gc();// try to force the bytes from the imgIn to be released

			// Create a new bitmap to load the bitmap again. Probably the memory will be available.
			imgIn = Bitmap.createBitmap(width, height, type);
			map.position(0);
			// load it back from temporary
			imgIn.copyPixelsFromBuffer(map);
			// close the temporary file and channel , then delete that also
			channel.close();
			randomAccessFile.close();

			// delete the temp file
			file.delete();

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

		return imgIn;
	}

	/**
	 * Used to detect both eyes colors
	 * 
	 * @param face
	 * @return an array of integers <br />
	 *         The first element represents the <b>left</b> eye color <br />
	 *         and the second one represents the <b>right</b> eye color
	 */
	private int[] detectEyesColor(Bitmap srcBitmap, Face face) {
		int[] eyeColors = new int[2];
		// Sets the position of the mid-point between the eyes.
		face.getMidPoint(midPoint);
		// Get the distance between the two eyes
		eyeDistance = face.eyesDistance();
		// Calculate the exact position of the eyes using the MidPoint
		int leftEye = srcBitmap.getPixel(Math.round(midPoint.x - eyeDistance / 2), Math.round(midPoint.y) + 15);
		int rightEye = srcBitmap.getPixel(Math.round(midPoint.x + eyeDistance / 2), Math.round(midPoint.y) + 15);
		// Get color of the two eyes
		int leftEyeColor = getColorUsingRGBComponents(leftEye);
		int rightEyeColor = getColorUsingRGBComponents(rightEye);
		// Put the two colors in an array and return it
		eyeColors[0] = leftEyeColor;
		eyeColors[1] = rightEyeColor;
		return eyeColors;
	}

	/**
	 * Returns the integer value of the eyes color using the passed <b>eyePixel</b> 's RGB components
	 * 
	 * @param eyePixel
	 * @return EyeColor integer value
	 */
	private int getColorUsingRGBComponents(int eyePixel) {
		int eyeRedComponent = Color.red(eyePixel);
		int eyeGreenComponent = Color.green(eyePixel);
		int eyeBlueComponenet = Color.blue(eyePixel);
		return Color.rgb(eyeRedComponent, eyeGreenComponent, eyeBlueComponenet);
	}
}
